﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JtextEpicsInterface;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections;

namespace UDPE
{
    public class udpe : IEJDriver
    {
        const int timeOut = 5000;

        private bool IsByte;
        private int CMDLen;
        private byte CMDEnd;

        public delegate void MSGArrivedHandler(byte[] MyMessage);
        MSGArrivedHandler MsgArrived = null;

        public event SendToEJServerHandler OnSendToEJServer;
        public event DriverCrashHandler OnDriverCrash;


        public delegate int OutMSGHandler(byte[] MyMessage);
        OutMSGHandler OutMSG = null;

        private object DrvIniObj;


        public Thread ListenerThread;
        
        #region udpSpecial

        UdpClient udpClient = null;
        UdpClient udpClientSend = null;
        string DName = "UDP";
        private IPEndPoint ipp;
        #endregion


        public string DriverName
        {
            get { return DName; }
        }

        public udpe()
        {
            //OutMSG = new OutMSGHandler(Out_msg);
            MsgArrived = new MSGArrivedHandler(MessageArrived);
        }


        public void SetDriver(bool isByte, int len, byte end, object IniObj)
        {
            IsByte = isByte;
            CMDLen = len;
            CMDEnd = end;
            if (IniObj != null)
            {
                DrvIniObj = IniObj;
                //ip and port
            }
            else
            {
                DrvIniObj = "115.156.252.255;1000;1001";
            }

        }

        public void IniDriver()
        {
            string iniString = Convert.ToString(DrvIniObj);
            string[] iniElement = iniString.Split(new char[] { ';' });
            IPAddress ip = IPAddress.Parse(iniElement[0]);
            int SendPort = int.Parse(iniElement[1]);
            int ReceivePort = int.Parse(iniElement[2]);

            ipp = new IPEndPoint(ip, SendPort);
            
            udpClientSend = new UdpClient();
            udpClient = new UdpClient(ReceivePort);
            
            
            try
            {
                this.StartListener();
                //Console.WriteLine("StartListener");
            }
            catch
            {
                Console.WriteLine("Can not start listener");
            }
        
        }


       

        public void StopDriver()
        {
            udpClient.Close();
            udpClientSend.Close();
            
        }
        public  void StopListener()
        {
            ListenerThread.Abort();
            ListenerThread = null;
        }


        #region  SendMessage



        public int Send_msg(byte[] message_byte)
        {
            //int i = udpClient.Send(message_byte, message_byte.Length, ipp);
            int i = udpClientSend.Send(message_byte, message_byte.Length, ipp);
            return i;
        }

        public int Send_msg(string message_str)
        {

            byte[] message = Encoding.Default.GetBytes(message_str.ToCharArray());
            byte[] test = new byte[message.Length + 1];
            int i;
            //#region testAddCMDend
            if (CMDLen==0)
            {
                for (int n = 0; n < message.Length; n++)
                {
                    test[n] = message[n];
                }
                test[message.Length] = CMDEnd;
               // asy = udpClient.BeginSend(test,test.Length, ipp, null, this);
                //i = udpClient.Send(test, test.Length, ipp); 
                i = udpClientSend.Send(test, test.Length, ipp);
            }
            //IAsyncResult asy;
          //  #endregion

           
            else
            {
                //asy = udpClient.BeginSend(message,message.Length, ipp, null, this);
                //i = udpClient.Send(message, message.Length, ipp);
                i = udpClientSend.Send(message, message.Length, ipp);
            }

            #region asy
            //asy.AsyncWaitHandle.WaitOne(timeOut, false);
            //if (!asy.IsCompleted)
            //{
            //    this.StopDriver();
            //    this.IniDriver();
            //    return -1;

            //}

            //int i = udpClient.EndSend(asy);

            #endregion
            return i;

        }

        #endregion

   
        #region ReceiveMessage


        private void MessageArrived(byte[] M)
        {
            if (OnSendToEJServer != null)
            {
                OnSendToEJServer(M);
            }

        }

        public string ReceiveMessageF()
        {
            IPEndPoint ip = new IPEndPoint(IPAddress.Any, 0);
            while (true)
            {
                try
                {
                    #region asy
                    //IAsyncResult asy = udpClient.BeginReceive(null, this);
                    //asy.AsyncWaitHandle.WaitOne(timeOut, false);
                    //if (!asy.IsCompleted)
                    //{
                    //    this.StopDriver();
                    //    this.IniDriver();
                    //    return "receive-timeout";

                    //}

                    //byte[] receiveByte = udpClient.EndReceive(asy, ref ip);
                    #endregion

                    byte[] receiveByte = udpClient.Receive(ref ip);

                    string receiveStr = Encoding.Default.GetString(receiveByte);

                    //Thread.Sleep(300);

                    // Console.WriteLine("receive");
                    return receiveStr;
                }
                catch
                {
                    throw new SystemException("Ass happened");
                }
            }
        }


        #endregion


        private void StartListener()
        {
            if (ListenerThread==null)
            {
                ListenerThread = null; 
            }
            ListenerThread = new Thread(new ThreadStart(this.Listener));
            ListenerThread.Start();


        }


        
        private void Listener()
        {
            ArrayList buffer=new ArrayList();
            byte[] msg;
            while (true)
            {

                string receiveMs = this.ReceiveMessageF();//Encoding.Default.GetBytes(receiveMs);
                byte[] receiveMb= Encoding.Default.GetBytes(receiveMs);

                Console.WriteLine(receiveMs);
                //Console.WriteLine("receiveMessage");
               

                #region cmdend
                if (CMDLen == 0)                                 //cmdlen has not been given but cmdend has         //  cmdend must be 0
                {
                    Console.WriteLine("cmdend");
                    if (receiveMs == "0")
                    {
                        continue;
                    }
                    else
                    {
                        for (int n = 0; n < receiveMb.Length; n++)
                        {
                            if (receiveMb[n] != CMDEnd)
                            {
                                buffer.Add(receiveMb[n]);
                            }
                            else
                            {
                                msg = (byte[])buffer.ToArray(typeof(byte));
                                //ReceiveMes = Encoding.Default.GetString(trans);
                                buffer.Clear();
                                break;
                            }
                        }
                    }

                }

                #endregion
               
                #region cmdlen
                else if (CMDLen > 0)
                {
                    //CMDLen have been given
                    Console.WriteLine("cmdlen");
                    if (receiveMb.Length == CMDLen)
                    {
                        MsgArrived.BeginInvoke(receiveMb, null, null);
                    }
                    else if (receiveMb.Length > CMDLen)
                    {
                        buffer.Clear();
                    }
                    else if (receiveMb.Length < CMDLen)
                    {
                        if (buffer.Count == 0)
                        {
                            for (int n = 0; n < receiveMb.Length; n++)
                            {
                                buffer.Add(receiveMb[n]);
                            }
                        }
                        else
                        {
                            if ((buffer.Count + receiveMb.Length) == CMDLen)
                            {
                                for (int n = 0; n < receiveMb.Length; n++)
                                {
                                    buffer.Add(receiveMb[n]);
                                }
                                msg = (byte[])buffer.ToArray(typeof(byte));
                                MsgArrived.BeginInvoke(msg, null, null);
                                buffer.Clear();
                            }
                            else if ((buffer.Count + receiveMb.Length) > CMDLen)
                            {
                                buffer.Clear();
                            }
                            else if ((buffer.Count + receiveMb.Length) < CMDLen)
                            {

                                for (int n = 0; n < receiveMb.Length; n++)
                                {
                                    buffer.Add(receiveMb[n]);
                                }
                            }
                        }
                    }
                }

#endregion
                else if (CMDLen < 0)
                {
                    //Console.WriteLine("no cmdlen cmdend");
                    MsgArrived.BeginInvoke(receiveMb, null, null);

                }
                
            }
        }
    }
}
